package sparrow.etl.core.util.tar;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.GZIPInputStream;

import sparrow.etl.core.exception.InvalidHeaderException;


/**
 *
 * <p>Title: </p>
 * <p>Description: </p>
 * <p>Copyright: Copyright (c) 2004</p>
 * <p>Company: </p>
 * @author not attributable
 * @version 1.0
 */
public
    class FastTarStream {
  private boolean debug = false;
  private boolean hasHitEOF = false;
  private TarEntry currEntry = null;
  private InputStream inStream = null;
  private int recordSize = TarBuffer.DEFAULT_RCDSIZE;

  public
      FastTarStream(InputStream in) {
    this(in, TarBuffer.DEFAULT_RCDSIZE);
  }

  public
      FastTarStream(InputStream in, int recordSize) {
    this.inStream = in;
    this.hasHitEOF = false;
    this.currEntry = null;
    this.recordSize = recordSize;
  }

  public void
      setDebug(boolean debug) {
    this.debug = debug;
  }

  public TarEntry
      getNextEntry() throws IOException {
    if (this.hasHitEOF) {
      return null;
    }

    /**
     * Here I have tried skipping the entry size, I have tried
     * skipping entrysize - header size,
     * entrysize + header, and all seem to skip to some bizzarelocation!
     */
    if (this.currEntry != null && this.currEntry.getSize() > 0) {
      // Need to round out the number of records to be read to skip entry...
      int numRecords =
          ( (int)this.currEntry.getSize() + (this.recordSize - 1))
          / this.recordSize;

      if (numRecords > 0) {
        this.inStream.skip(numRecords * this.recordSize);
      }
    }

    byte[] headerBuf = new byte[this.recordSize];

    // NOTE Apparently (with GZIPInputStream maybe?) we are able to
    //      read less then record size bytes in any given read(). So,
    //      we have to be persistent.

    int bufIndex = 0;
    for (int bytesNeeded = this.recordSize; bytesNeeded > 0; ) {
      int numRead = this.inStream.read(headerBuf, bufIndex, bytesNeeded);

      if (numRead == -1) {
        this.hasHitEOF = true;
        break;
      }

      bufIndex += numRead;
      bytesNeeded -= numRead;
    }

    // Check for "EOF block" of all zeros
    if (!this.hasHitEOF) {
      this.hasHitEOF = true;
      for (int i = 0; i < headerBuf.length; ++i) {
        if (headerBuf[i] != 0) {
          this.hasHitEOF = false;
          break;
        }
      }
    }

    if (this.hasHitEOF) {
      this.currEntry = null;
    }
    else {
      try {
        this.currEntry = new TarEntry(headerBuf);

        if (this.debug) {
          byte[] by = new byte[headerBuf.length];
          for (int i = 0; i < headerBuf.length; i++) {
            by[i] = (headerBuf[i] == 0 ? 20 : headerBuf[i]);
          }
          String s = new String(by);
          System.out.println("\n" + s);
        }

        if (! (headerBuf[257] == 'u' && headerBuf[258] == 's'
               && headerBuf[259] == 't' && headerBuf[260] == 'a'
               && headerBuf[261] == 'r')) {
          throw new InvalidHeaderException
              ("header magic is not'ustar', but '"
               + headerBuf[257] + headerBuf[258] + headerBuf[259]
               + headerBuf[260] + headerBuf[261] + "', or (dec) "
               + ( (int) headerBuf[257]) + ", "
               + ( (int) headerBuf[258]) + ", "
               + ( (int) headerBuf[259]) + ", "
               + ( (int) headerBuf[260]) + ", "
               + ( (int) headerBuf[261]));
        }
      }
      catch (InvalidHeaderException ex) {
        this.currEntry = null;
        throw ex;
      }
    }

    return this.currEntry;
  }

  public static void
      main(String[] args) {
    boolean debug = false;
    InputStream in = null;

    String fileName = args[0];

    try {
      int idx = 0;
      if (args.length > 0) {
        if (args[idx].equals("-d")) {
          debug = true;
          idx++;
        }

        if (args[idx].endsWith(".gz")
            || args[idx].endsWith(".tgz")) {
          in = new GZIPInputStream(new FileInputStream(args[idx]));
        }
        else {
          in = new FileInputStream(args[idx]);
        }
      }
      else {
        in = System.in;
      }

      FastTarStream fts = new FastTarStream(in);
      fts.setDebug(debug);

      int nameWidth = 56;
      int sizeWidth = 9;
      int userWidth = 8;
      StringBuffer padBuf = new StringBuffer(128);
      for (; ; ) {
        TarEntry entry = fts.getNextEntry();
        if (entry == null) {
          break;
        }

        if (entry.isDirectory()) {
          // TYPE
          System.out.print("D ");

          // NAME
          padBuf.setLength(0);
          padBuf.append(entry.getName());
          padBuf.setLength(padBuf.length() - 1); // drop '/'
          if (padBuf.length() > nameWidth) {
            padBuf.setLength(nameWidth);
          }
          for (; padBuf.length() < nameWidth; ) {
            padBuf.append('_');

          }
          padBuf.append('_');
          System.out.print(padBuf.toString());

          // SIZE
          padBuf.setLength(0);
          for (; padBuf.length() < sizeWidth; ) {
            padBuf.insert(0, '_');

          }
          padBuf.append(' ');
          System.out.print(padBuf.toString());

          // USER
          padBuf.setLength(0);
          padBuf.append(entry.getUserName());
          if (padBuf.length() > userWidth) {
            padBuf.setLength(userWidth);
          }
          for (; padBuf.length() < userWidth; ) {
            padBuf.append(' ');

          }
          System.out.print(padBuf.toString());
        }
        else {
          // TYPE
          System.out.print("F ");

          // NAME
          padBuf.setLength(0);
          padBuf.append(entry.getName());
          if (padBuf.length() > nameWidth) {
            padBuf.setLength(nameWidth);
          }
          for (; padBuf.length() < nameWidth; ) {
            padBuf.append(' ');

          }
          padBuf.append(' ');
          System.out.print(padBuf.toString());

          // SIZE
          padBuf.setLength(0);
          padBuf.append(entry.getSize());
          if (padBuf.length() > sizeWidth) {
            padBuf.setLength(sizeWidth);
          }
          for (; padBuf.length() < sizeWidth; ) {
            padBuf.insert(0, ' ');

          }
          padBuf.append(' ');
          System.out.print(padBuf.toString());

          // USER
          padBuf.setLength(0);
          padBuf.append(entry.getUserName());
          if (padBuf.length() > userWidth) {
            padBuf.setLength(userWidth);
          }
          for (; padBuf.length() < userWidth; ) {
            padBuf.append(' ');

          }
          System.out.print(padBuf.toString());
        }

        System.out.println("");
      }
    }
    catch (IOException ex) {
      ex.printStackTrace(System.err);
    }
  }

}
